package org.tech4j.edi.base;

import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.tech4j.edi.api.RetryExecutorChain;
import org.tech4j.edi.api.RetryRecordService;
import org.tech4j.edi.api.response.ResponseCode;
import org.tech4j.edi.base.threadpool.RetryThreadPoolFactory;
import org.tech4j.edi.common.entity.RetryBizConfig;
import org.tech4j.edi.common.entity.RetryBizRecord;
import org.tech4j.edi.api.context.RetryContext;
import org.tech4j.edi.common.enums.RetryStatusEnum;
import org.tech4j.edi.common.result.EdiResult;
import org.tech4j.edi.persistence.api.RetryConfigPersistService;
import org.tech4j.edi.persistence.api.RetryRecordPersistService;
import org.tech4j.edi.persistence.api.query.RetryConfigQuery;
import org.tech4j.edi.persistence.api.query.RetryRecordQuery;

import java.time.LocalDateTime;
import java.util.List;

/**
 * @program: tech4j
 * @description:
 * @author: YuKai Fan
 * @create: 2025/3/7 11:30
 **/
public class RetryRecordExecutorService implements RetryRecordService {
    private static final Logger log = LoggerFactory.getLogger(RetryRecordExecutorService.class);
    private final RetryExecutorChain retryExecutorChain;
    private final RetryConfigPersistService retryConfigPersistService;
    private final RetryRecordPersistService retryRecordPersistService;

    public RetryRecordExecutorService(RetryExecutorChain retryExecutorChain, RetryConfigPersistService retryConfigPersistService, RetryRecordPersistService retryRecordPersistService) {
        this.retryExecutorChain = retryExecutorChain;
        this.retryConfigPersistService = retryConfigPersistService;
        this.retryRecordPersistService = retryRecordPersistService;
    }

    @Override
    public EdiResult<String> addRetryRecord(RetryBizRecord retryBizRecord) {
        return retryRecordPersistService.persistRetryRecord(retryBizRecord);
    }

    @Override
    public EdiResult<String> addRetryRecord(RetryBizRecord retryBizRecord, boolean autoExe) {
        EdiResult<String> result = this.addRetryRecord(retryBizRecord);
        if (result.getSuccess() && autoExe) {
            boolean actualTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
            // 判断当前方法在事务中，才会执行
            if (actualTransactionActive) {
                EdiResult<RetryBizConfig> retryConfigResult = retryConfigPersistService.getOneRetryConfig(RetryConfigQuery.Builder.builder()
                        .bizType(retryBizRecord.getBizType())
                        .env(retryBizRecord.getEnv())
                        .build()
                );
                if (retryConfigResult.isFailed()) {
                    return EdiResult.ofFailed0(retryConfigResult.getCode(), retryConfigResult.getMessage());
                }
                RetryThreadPoolFactory.getThreadPoolExecutor()
                        .execute(() -> {
                            RetryContext retryContext = new RetryContext(retryBizRecord, retryConfigResult.getData());
                            retryExecutorChain.execute(retryContext);
                        });
            } else {
                log.warn("RetryRecordService[addRetryRecord] auto execute retry failed, cause The current method is not in the transaction");
            }
        }
        return result;
    }

    @Override
    public EdiResult<Void> retry(List<String> idList, String bizType, LocalDateTime startTime, boolean force) {
        RetryRecordQuery recordQuery = RetryRecordQuery.Builder.builder()
                .idList(idList)
                .bizType(bizType)
                .build();
        List<RetryBizRecord> retryBizRecordList = retryRecordPersistService.list(recordQuery);
        if (CollectionUtils.isEmpty(retryBizRecordList)) {
            return EdiResult.ofFailed0(ResponseCode.NOT_FOUND);
        }
        EdiResult<RetryBizConfig> retryConfigResult = retryConfigPersistService.getOneRetryConfig(RetryConfigQuery.Builder.builder().bizType(bizType).build());
        if (retryConfigResult.isFailed()) {
            return EdiResult.ofFailed0(retryConfigResult.getCode(), retryConfigResult.getMessage());
        }
        List<RetryContext> retryContextList = retryBizRecordList.stream()
                .map(retryBizRecord -> new RetryContext(retryBizRecord, retryConfigResult.getData(), force))
                .toList();
        RetryThreadPoolFactory.getThreadPoolExecutor()
                .execute(() -> {
                    retryContextList.forEach(retryExecutorChain::execute);
                });
        return EdiResult.ofSuccess();
    }

    @Override
    public EdiResult<Void> retry(List<RetryBizRecord> retryBizRecordList) {
        for (RetryBizRecord retryBizRecord : retryBizRecordList) {
            EdiResult<RetryBizConfig> retryConfigResult = retryConfigPersistService.getOneRetryConfig(
                    RetryConfigQuery.Builder.builder()
                            .bizType(retryBizRecord.getBizType())
                            .env(retryBizRecord.getEnv())
                            .build()
            );
            if (retryConfigResult.isFailed()) {
                return EdiResult.ofFailed0(retryConfigResult.getCode(), retryConfigResult.getMessage());
            }
            RetryThreadPoolFactory.getThreadPoolExecutor()
                    .execute(() -> {
                        RetryContext retryContext = new RetryContext(retryBizRecord, retryConfigResult.getData());
                        retryExecutorChain.execute(retryContext);
                    });
        }
        return EdiResult.ofSuccess();
    }

    @Override
    public EdiResult<Void> close(String id, String bizType, LocalDateTime startTime, String remark) {
        RetryBizRecord retryBizRecord = retryRecordPersistService.getRetryBizRecordById(id, bizType);
        if (retryBizRecord == null) {
            return EdiResult.ofFailed0(ResponseCode.NOT_FOUND);
        }
        return retryRecordPersistService.updateRetryStatusAndRemark(retryBizRecord, RetryStatusEnum.RETRY_CLOSE, remark);
    }
}
